# PCA 降维 人脸识别
# 采用SVM 分类
from sklearn.datasets import fetch_lfw_people
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA

# 读入数据  #######################################
faces = fetch_lfw_people(min_faces_per_person=50) # 提取人脸个数至少是50以上的人脸
# 第一次需要较长时间下载，晚一点提供数据链接

# 显示数据的规模 #######################################
print(dir(faces))#查看数据的属性
print(faces.data.shape)
print(faces.images.shape)
print(faces.target.shape)
print(faces.target_names.shape)
print(faces.target_names)# 给出人脸的名字

input()
shape = faces.images.shape

# 画出12个人脸 #######################################
fig, axes = plt.subplots(2, 6, figsize=(15, 8),
subplot_kw = {'xticks': (), 'yticks': ()})
for target, image, ax in zip(faces.target, faces.images, axes.ravel()):
    ax.imshow(image, cmap='gray')  #, cmap='Greys'  bone
    ax.set_title(faces.target_names[target])
plt.subplots_adjust(wspace =0.1, hspace =0.1)#调整子图间距
fig.suptitle('前12个人脸')
# plt.show()

# 统计 每一个名字 有多少个人脸 #######################################
counts = np.bincount(faces.target)
for i, (count, name) in enumerate(zip(counts, faces.target_names)):
    print("{0:25} {1:3}".format(name, count), end = '   ')
    if (i + 1) % 2 == 0:
        print()

# 由于Bush的人脸太多，样本不平衡，最多只取50张人脸#######################################
mask = np.zeros(faces.target.shape, dtype=bool)
for target in np.unique(faces.target):
    mask[np.where(faces.target == target)[0][:50]] = 1

x_data = faces.data[mask] / 255.  # 将灰度值转换为[0, 1]区间
y_data = faces.target[mask]

# 拆分训练集与测试集 #######################################
x_train, x_test, y_train, y_test = train_test_split(
        x_data, y_data, test_size=0.2, random_state=42)

print(x_train.shape, x_test.shape) # (480, 2914) (120, 2914)
# input()

# 用训练集进行PCA降维 #######################################
n_components = 100 #提取的主成分数量
print("提取的 top %d eigenfaces from %d faces" % (n_components, x_train.shape[0]))

pca = PCA(n_components=n_components, svd_solver='randomized', whiten=False)
pca.fit(x_train) #用训练集进行 特征提取, 不是用全部数据

eigenfaces = pca.components_.reshape((n_components,
                                      faces.images.shape[1],
                                      faces.images.shape[2])) #低维空间（PCA中的矩阵W）

print(pca.components_.shape, eigenfaces.shape)


# 画出前12个特征脸 #######################################
fig, axes = plt.subplots(2, 6, figsize=(15, 8), subplot_kw = {'xticks': (), 'yticks': ()})
for image, ax in zip(eigenfaces, axes.ravel()):
    ax.imshow(image, cmap='gray')  #, cmap='Greys'
plt.subplots_adjust(wspace =0.1, hspace =0.1)#调整子图间距
fig.suptitle('12个特征脸')
# plt.show()

# 画出累计方差 #######################################
plt.figure()
csum = np.cumsum(pca.explained_variance_ratio_)
plt.plot(range(1, n_components+1), csum, 'ro-', label='累计方差')
plt.title('方差图')
plt.xlabel('主成分')
plt.ylabel('方差')
# plt.plot(range(1, n_components+1),  pca.explained_variance_ratio_, 'bo-', label='单个方差')
plt.bar(range(1, n_components+1),  pca.explained_variance_ratio_, label='单个方差')
plt.legend()
# plt.show()

# 将训练集与测试集投影到低维空间， 并采用SVM分类 #######################################
print("把训练集与测试集 投影到低维空间")
X_train_pca = pca.transform(x_train) #将训练集投影到低维空间
X_test_pca = pca.transform(x_test)

print("X_train_pca:",X_train_pca.shape)
print("X_test_pca:",X_test_pca.shape)



# 采用SVM 建立模型#######################################
from sklearn.svm import SVC

# clf = SVC(C=1.0)  # 默认SVM设置  rbf核函数
clf = SVC(C=1.0, degree=3, kernel='poly')   # 3次多项式核函数
# clf = SVC(C=1.0, kernel='linear')           # 线性核函数
# clf = SVC(kernel='rbf')           # 高斯核


clf.fit(X_train_pca, y_train)
# print(clf.support_vectors_)   # 输出支持向量

# 给出 评价、混淆矩阵、分类报告#######################################
score=clf.score(X_train_pca, y_train)
print(score)
score=clf.score(X_test_pca, y_test)              # 评估测试集
print(score)

y_pred = clf.predict(X_test_pca)

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix

print(classification_report(y_test, y_pred, target_names=faces.target_names))
print(confusion_matrix(y_test, y_pred, labels=range(faces.target_names.shape[0])))

# 人脸重构，前50个特征脸重构前12个脸 #####################################
ww = pca.transform(x_data[:12, :])   # (12, 50)
print(ww.shape, pca.components_.shape)

reconF = ww @ pca.components_    # 重构  (12, 2914)
reconF = reconF.reshape((12, faces.images.shape[1],
                             faces.images.shape[2])) # 转成(12,62, 47)

fig, axes = plt.subplots(2, 6, figsize=(15, 8), subplot_kw = {'xticks': (), 'yticks': ()})
for image, ax in zip(reconF, axes.ravel()):
    ax.imshow(image, cmap='gray')  #, cmap='Greys'
plt.subplots_adjust(wspace =0.1, hspace =0.1)#调整子图间距
fig.suptitle('用前50个特征脸重构 前12张图片')
# input()

#####################################################################
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负
plt.show()
print('done')